#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
using namespace std;

namespace ns_blockqueue
{
    const int default_cap = 5;
    template <class T>
    class BlockQueue
    {
    private:
        queue<T> _bq;             // 阻塞队列
        int _cap;                 // 队列元素上限
        pthread_mutex_t _mtx;     // 保护临界资源的锁
        pthread_cond_t _is_full;  //_bq满了,消费者在该条件变量下等待
        pthread_cond_t _is_empty; //_bq空的,生产者在该条件变量下等待

    private:
        bool IsFull()
        {
            return _bq.size() == _cap;
        }
        bool IsEmpty()
        {
            return _bq.size() == 0;
        }
        void LockQueue()
        {
            pthread_mutex_lock(&_mtx);
        }
        
        void UnlockQueue()
        {
            pthread_mutex_unlock(&_mtx);
        }

        void ProducterWait()
        {
            // 1.调用的时候先把锁释放了,再挂起自己
            // 2.返回的时候,先竞争锁,把锁竞争到了再返回
            pthread_cond_wait(&_is_empty, &_mtx);
        }

        void ConsumerWait()
        {
            pthread_cond_wait(&_is_full, &_mtx);
        }

        void WakeupProducter()
        {
            pthread_cond_signal(&_is_empty);
        }

        void WakeupConsumer()
        {
            pthread_cond_signal(&_is_full);
        }
       
    public:
        BlockQueue(int cap = default_cap)
            : _cap(cap)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_is_full, nullptr);
            pthread_cond_init(&_is_empty, nullptr);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_is_full);
            pthread_cond_destroy(&_is_empty);
        }

        // const &:输入  *：输出  &：输入输出
    public:
        void Push(const T &in)
        {
            LockQueue();

            // 进行条件检测的时候,用循环的方式
            // 当退出循环的时候一定是因为条件不满足
            while (IsFull())
            {
                ProducterWait();
            }

            _bq.push(in);
            WakeupConsumer(); // 通知消费者来消费

            UnlockQueue();
        }

        void Pop(T *out)
        {
            LockQueue();

            while (IsEmpty())
            {
                ConsumerWait();
            }

            *out = _bq.front();
            _bq.pop();
            WakeupProducter(); // 通知生产者生产

            UnlockQueue();
        }
    };
}